/**
 * File: MovableText.cpp
 *
 * description: This create create a billboarding object that display a text.
 * 
 * @author  2003 by cTh see gavocanov@rambler.ru
 * @update  2006 by barraq see nospam@barraquand.com
 */
 
#include "stdafx.h"
#include "Ogre.h"

#include "movableTexture.h"
 
using namespace Ogre;
 
#define POS_TEX_BINDING    0
#define COLOUR_BINDING     1

#define Y_UP
 
MovableTexture::MovableTexture(const String &name, const String &fileName, float height, float width
							   , float offsetX
							   , const ColourValue &color
							    )
: mpCam(NULL)
, mpWin(NULL)
, mName(name)
, mFileName( fileName )
, mColor(color)
, mType("MovableTexture")
, mTimeUntilNextToggle(0)
, mUpdateColors(true)
, mOnTop(false)
, mbAutoScale(false)
, mScaleK(0.1f)
, mHorizontalAlignment(H_LEFT)
, mVerticalAlignment(V_BELOW)
, mGlobalTranslation(0.0)
, mLocalTranslation(0.0)
, mHeight(height)
, mWidth(width)
, mOffsetX(offsetX)
{
    if (name == "")
        throw Exception(Exception::ERR_INVALIDPARAMS, "Trying to create MovableText without name", "MovableText::MovableText");
 
 
    mRenderOp.vertexData = NULL;

    this->setTexName(mFileName);
//	this->_setupGeometryTest();
	this->_setupGeometry();
}
 
MovableTexture::~MovableTexture()
{
    if (mRenderOp.vertexData)
        delete mRenderOp.vertexData;
    // May cause crashing... check this and comment if it does
    if (!mpMaterial.isNull())
        MaterialManager::getSingletonPtr()->remove(mpMaterial->getName());
}

void MovableTexture::setPlaneSize( float height, float width )
{
	mHeight = height;
	mWidth = width;
	
	_setupGeometry();
}
 
void MovableTexture::setTexName( const String &fileName)
{
    if((Ogre::MaterialManager::getSingletonPtr()->resourceExists(mName + "Material"))) 
    { 
        Ogre::MaterialManager::getSingleton().remove(mName + "Material"); 
    }

	if( mFileName!= fileName || mpMaterial.isNull() )
	{
		mFileName = fileName;

		if (!mpMaterial.isNull())
        {
            MaterialManager::getSingletonPtr()->remove(mpMaterial->getName());
            mpMaterial.setNull();
        }

		Ogre::String name = mName + fileName + "_Material";
		mpMaterial = MaterialManager::getSingleton().create( name, "General" );
		mpMaterial->getTechnique( 0 )->getPass( 0 )->createTextureUnitState( fileName );

		if (!mpMaterial->isLoaded())
			mpMaterial->load();

		mpMaterial->setDepthCheckEnabled(!mOnTop);
		mpMaterial->setSceneBlending(SBT_TRANSPARENT_ALPHA);
		mpMaterial->setDepthBias(1.0,1.0);
		mpMaterial->setDepthWriteEnabled(mOnTop);
		mpMaterial->setLightingEnabled(false);
		mNeedUpdate = true;
	}
 
   
}

void MovableTexture::setAutoScale( bool bAutoscale, float AutoscaleK )
{
	mbAutoScale = bAutoscale;

	if( AutoscaleK - 0.f > 0)
	{
		mScaleK = AutoscaleK;
	}
}
 
 
void MovableTexture::setColor(const ColourValue &color)
{
    if (color != mColor)
    {
        mColor = color;
        mUpdateColors = true;
    }
}
 
void MovableTexture::setGlobalTranslation( Vector3 trans )
{
    mGlobalTranslation = trans;
}
 
void MovableTexture::setLocalTranslation( Vector3 trans )
{
    mLocalTranslation = trans;
}
 
void MovableTexture::showOnTop(bool show)
{
    if( mOnTop != show && !mpMaterial.isNull() )
    {
        mOnTop = show;
        mpMaterial->setDepthBias(1.0,1.0);
        mpMaterial->setDepthCheckEnabled(!mOnTop);
        mpMaterial->setDepthWriteEnabled(mOnTop);
    }
}

void MovableTexture::setAdditionalHeight( float height )
{
#ifdef	Y_UP
	mGlobalTranslation.y = height;
#else
	mGlobalTranslation.z = height;
#endif

	mNeedUpdate = true;
}
 
void MovableTexture::_setupGeometry()
{
    assert(!mpMaterial.isNull());
 
    unsigned int vertexCount = static_cast<unsigned int>(1 * 6);
 
    if (mRenderOp.vertexData)
    {
        {
            delete mRenderOp.vertexData;
            mRenderOp.vertexData = NULL;
            mUpdateColors = true;
        }
    }
 
    if (!mRenderOp.vertexData)
        mRenderOp.vertexData = new VertexData();
 
    mRenderOp.indexData = 0;
    mRenderOp.vertexData->vertexStart = 0;
    mRenderOp.vertexData->vertexCount = vertexCount;
    mRenderOp.operationType = RenderOperation::OT_TRIANGLE_LIST; 
    mRenderOp.useIndexes = false; 
 
    VertexDeclaration  *decl = mRenderOp.vertexData->vertexDeclaration;
    VertexBufferBinding   *bind = mRenderOp.vertexData->vertexBufferBinding;
    size_t offset = 0;
 
    // create/bind positions/tex.ccord. buffer
    if (!decl->findElementBySemantic(VES_POSITION))
        decl->addElement(POS_TEX_BINDING, offset, VET_FLOAT3, VES_POSITION);
 
    offset += VertexElement::getTypeSize(VET_FLOAT3);
 
    if (!decl->findElementBySemantic(VES_TEXTURE_COORDINATES))
        decl->addElement(POS_TEX_BINDING, offset, Ogre::VET_FLOAT2, Ogre::VES_TEXTURE_COORDINATES, 0);
 
    HardwareVertexBufferSharedPtr ptbuf = HardwareBufferManager::getSingleton().createVertexBuffer(decl->getVertexSize(POS_TEX_BINDING),
        mRenderOp.vertexData->vertexCount,
        HardwareBuffer::HBU_DYNAMIC_WRITE_ONLY);
    bind->setBinding(POS_TEX_BINDING, ptbuf);
 
    if (!decl->findElementBySemantic(VES_DIFFUSE))
        decl->addElement(COLOUR_BINDING, 0, VET_COLOUR, VES_DIFFUSE);
 
    HardwareVertexBufferSharedPtr cbuf = HardwareBufferManager::getSingleton().createVertexBuffer(decl->getVertexSize(COLOUR_BINDING),
        mRenderOp.vertexData->vertexCount,
        HardwareBuffer::HBU_DYNAMIC_WRITE_ONLY);
    bind->setBinding(COLOUR_BINDING, cbuf);
	float *pPCBuff = static_cast<float*>(ptbuf->lock(HardwareBuffer::HBL_DISCARD));
 
    
    // Raise the first line of the caption
 
	
 
    Real u1 = 0.f, u2 = 1.f, v1 = 0.f, v2 = 1.f; 
	Real left = -mWidth/2.f, top = mHeight/2.f;
//	Real width = 0.67f, height = 0.27f;

    // each vert is (x, y, z, u, v)
    //-------------------------------------------------------------------------------------
    // First tri
    //
    // Upper left
  
    *pPCBuff++ = left + mOffsetX; 
    *pPCBuff++ = top;
    *pPCBuff++ = 0.0;
    *pPCBuff++ = u1;
    *pPCBuff++ = v1;

    *pPCBuff++ = left + mOffsetX; 
    *pPCBuff++ = -top;
    *pPCBuff++ = 0.0;
    *pPCBuff++ = u1;
    *pPCBuff++ = v2;

    // Deal with bounds
    
    *pPCBuff++ = -left + mOffsetX;
    *pPCBuff++ = top;
    *pPCBuff++ = 0.0;
    *pPCBuff++ = u2;
    *pPCBuff++ = v1;
	
	/////

    *pPCBuff++ = -left + mOffsetX;
    *pPCBuff++ = top;
    *pPCBuff++ = 0.0;
    *pPCBuff++ = u2;
    *pPCBuff++ = v1;
    
    *pPCBuff++ = left + mOffsetX;  
    *pPCBuff++ = -top;
    *pPCBuff++ = 0.0;
    *pPCBuff++ = u1;
    *pPCBuff++ = v2;
    
    *pPCBuff++ = -left + mOffsetX;
    *pPCBuff++ = -top;
    *pPCBuff++ = 0.0;
    *pPCBuff++ = u2;
    *pPCBuff++ = v2;
    //-------------------------------------------------------------------------------------

  
 
    // Unlock vertex buffer
    ptbuf->unlock();
 
    // update AABB/Sphere radius
	Vector3 min( -2.f*mWidth, -1.f, -1.f );
	Vector3 max( mWidth, 1.f, -1.f );
    mAABB = Ogre::AxisAlignedBox(min, max);
	Ogre::Real maxSquaredRadius = 2.f*mWidth *2.f*mWidth;
    mRadius = Ogre::Math::Sqrt(maxSquaredRadius);
 
    if (mUpdateColors)
        this->_updateColors();
 
    mNeedUpdate = false;
}


void MovableTexture::_updateColors(void)
{
    assert(!mpMaterial.isNull());
 
    // Convert to system-specific
    RGBA color;
    Root::getSingleton().convertColourValue(mColor, &color);
    HardwareVertexBufferSharedPtr vbuf = mRenderOp.vertexData->vertexBufferBinding->getBuffer(COLOUR_BINDING);
    RGBA *pDest = static_cast<RGBA*>(vbuf->lock(HardwareBuffer::HBL_DISCARD));
    for (int i = 0; i < (int)mRenderOp.vertexData->vertexCount; ++i)
        *pDest++ = color;
    vbuf->unlock();
    mUpdateColors = false;
}
 
const Quaternion& MovableTexture::getWorldOrientation(void) const
{
    assert(mpCam);
    return const_cast<Quaternion&>(mpCam->getDerivedOrientation());
}
 
// Add to build on Shoggoth:
void MovableTexture::visitRenderables(Ogre::Renderable::Visitor* visitor, 
        bool debugRenderables)
{
}
 
const Vector3& MovableTexture::getWorldPosition(void) const
{
    assert(mParentNode);
    return mParentNode->_getDerivedPosition();
}
 
void MovableTexture::getWorldTransforms(Matrix4 *xform) const 
{
    if (this->isVisible() && mpCam)
    {
        Matrix3 rot3x3, scale3x3 = Matrix3::IDENTITY;
 
        // store rotation in a matrix
        mpCam->getDerivedOrientation().ToRotationMatrix(rot3x3);
 
        // parent node position
  //      Vector3 ppos = mParentNode->_getDerivedPosition() + Vector3::UNIT_Y*mGlobalTranslation;
		 Vector3 ppos = mParentNode->_getDerivedPosition() + mGlobalTranslation;
        ppos += rot3x3*mLocalTranslation;

        // apply scale
        scale3x3[0][0] = mParentNode->_getDerivedScale().x / 2;
        scale3x3[1][1] = mParentNode->_getDerivedScale().y / 2;
        scale3x3[2][2] = mParentNode->_getDerivedScale().z / 2;

		if( mbAutoScale )
		{
			Matrix4 camMatrix;
			mpCam->getWorldTransforms( &camMatrix );
			Vector3 myPos = this->getWorldPosition();
			Vector3 curPos = camMatrix.getTrans();
			Vector3 dis( myPos.x - curPos.x, myPos.y - curPos.y
				, myPos.z - curPos.z );
			float scale_k = dis.length();
			scale_k *= mScaleK;

			scale3x3[0][0] *= scale_k;
			scale3x3[1][1] *= scale_k;
			scale3x3[2][2] *= scale_k;
		}
 
        // apply all transforms to xform       
        *xform = (rot3x3 * scale3x3);
        xform->setTrans(ppos);
    }
}
 
void MovableTexture::getRenderOperation(RenderOperation &op)
{
    if (this->isVisible())
    {
        if (mNeedUpdate)
            this->_setupGeometry();
        if (mUpdateColors)
            this->_updateColors();
        op = mRenderOp;
    }
}
 
void MovableTexture::_notifyCurrentCamera(Camera *cam)
{
    mpCam = cam;
}
 
void MovableTexture::_updateRenderQueue(RenderQueue* queue)
{
    if (this->isVisible())
    {
        if (mNeedUpdate)
            this->_setupGeometry();
        if (mUpdateColors)
            this->_updateColors();
 
        queue->addRenderable(this, mRenderQueueID, OGRE_RENDERABLE_DEFAULT_PRIORITY);
        //queue->addRenderable(this, mRenderQueueID, RENDER_QUEUE_SKIES_LATE);
    }
}